package processor

import (
	"fmt"
	"sync"
	"time"
)

// 任务调度
type Scheduler struct {
	jobEventChan      chan *JobEvent              //  etcd任务事件队列
	JobPlanTable      map[string]*JobSchedulePlan // 任务调度计划表
	jobExecutingTable map[string]*JobExecuteInfo  // 任务执行表
	jobResultChan     chan *JobExecuteResult      // 任务结果队列
}

var (
	G_scheduler *Scheduler
	G_mux       sync.RWMutex
)

// 处理任务事件
func (scheduler *Scheduler) HandleJobEvent(jobEvent *JobEvent) {
	var (
		jobSchedulePlan *JobSchedulePlan
		err             error
	)

	if jobSchedulePlan, err = BuildJobSchedulePlan(jobEvent.Job); err != nil {
		return
	}

	G_mux.RLock()
	scheduler.JobPlanTable[jobEvent.Job.Name] = jobSchedulePlan
	G_mux.RUnlock()
}

// 尝试执行任务
func (scheduler *Scheduler) TryStartJob(jobPlan *JobSchedulePlan) {
	// 调度 和 执行 是2件事情
	var (
		jobExecuteInfo *JobExecuteInfo
		jobExecuting   bool
	)

	// 执行的任务可能运行很久, 1分钟会调度60次，但是只能执行1次, 防止并发！

	// 如果任务正在执行，跳过本次调度
	if jobExecuteInfo, jobExecuting = scheduler.jobExecutingTable[jobPlan.Job.Name]; jobExecuting {
		// fmt.Println("尚未退出,跳过执行:", jobPlan.Job.Name)
		return
	}

	// 构建执行状态信息
	jobExecuteInfo = BuildJobExecuteInfo(jobPlan)

	// 保存执行状态
	scheduler.jobExecutingTable[jobPlan.Job.Name] = jobExecuteInfo

	// 执行任务
	//fmt.Println("执行任务:", jobExecuteInfo.Job.Name, jobExecuteInfo.PlanTime, jobExecuteInfo.RealTime)
	G_executor.ExecuteJob(jobExecuteInfo)

}

// 重新计算任务调度状态
func (scheduler *Scheduler) TrySchedule() (scheduleAfter time.Duration) {
	var (
		jobPlan      *JobSchedulePlan
		now          time.Time
		nearTime     *time.Time
		jobPlanSlice []string
		value        string
	)

	// 如果任务表为空话，随便睡眠多久
	if len(scheduler.JobPlanTable) == 0 {
		scheduleAfter = 1 * time.Second
		return
	}

	// 当前时间
	now = time.Now()

	// 遍历所有任务
	G_mux.RLock()
	for _, jobPlan = range scheduler.JobPlanTable {
		if jobPlan.RunAt.Before(now) || jobPlan.RunAt.Equal(now) {
			fmt.Println("删除前scheduler.jobPlanTable:", scheduler.JobPlanTable)
			scheduler.TryStartJob(jobPlan)
			//删除任务表中的任务
			jobPlanSlice = append(jobPlanSlice, jobPlan.Job.Name)

		}

		//// 统计最近一个要过期的任务时间
		if nearTime == nil || jobPlan.RunAt.Before(*nearTime) {
			nearTime = &jobPlan.RunAt
		}
	}

	for _, value = range jobPlanSlice {
		delete(scheduler.JobPlanTable, value)
		fmt.Println("删除后前scheduler.jobPlanTable:", scheduler.JobPlanTable)
	}
	G_mux.RUnlock()

	// 下次调度间隔（最近要执行的任务调度时间 - 当前时间）
	scheduleAfter = (*nearTime).Sub(now)
	return
}

// 处理任务结果
func (scheduler *Scheduler) handleJobResult(result *JobExecuteResult) {
	var (
		jobLog *JobLog
	)
	// 删除执行状态
	delete(scheduler.jobExecutingTable, result.ExecuteInfo.Job.Name)

	// 生成执行日志
	jobLog = &JobLog{
		JobName:      result.ExecuteInfo.Job.Name,
		Command:      result.ExecuteInfo.Job.Command,
		Output:       string(result.Output),
		PlanTime:     result.ExecuteInfo.PlanTime.UnixNano() / 1000 / 1000,
		ScheduleTime: result.ExecuteInfo.RealTime.UnixNano() / 1000 / 1000,
		StartTime:    result.StartTime.UnixNano() / 1000 / 1000,
		EndTime:      result.EndTime.UnixNano() / 1000 / 1000,
	}
	if result.Err != nil {
		jobLog.Err = result.Err.Error()
	} else {
		jobLog.Err = ""
	}
	G_logSink.Append(jobLog)

	// fmt.Println("任务执行完成:", result.ExecuteInfo.Job.Name, string(result.Output), result.Err)
}

// 调度协程
func (scheduler *Scheduler) scheduleLoop() {
	var (
		//jobEvent *JobEvent
		scheduleAfter time.Duration
		scheduleTimer *time.Timer
		jobResult     *JobExecuteResult
	)

	// 初始化一次(1秒)
	//scheduleAfter = scheduler.TrySchedule()

	// 调度的延迟定时器
	scheduleTimer = time.NewTimer(scheduleAfter)

	// 定时任务common.Job
	for {
		select {

		case <-scheduleTimer.C: // 最近的任务到期了
		case jobResult = <-scheduler.jobResultChan: // 监听任务执行结果
			//fmt.Println("jobResult:任务结构")
			scheduler.handleJobResult(jobResult)
		}
		// 调度一次任务
		scheduleAfter = scheduler.TrySchedule()
		// 重置调度间隔
		scheduleTimer.Reset(scheduleAfter)
	}
}

// 推送任务变化事件
func (scheduler *Scheduler) PushJobEvent(jobEvent *JobEvent) {
	scheduler.jobEventChan <- jobEvent
}

// 初始化调度器
func InitScheduler() (err error) {
	G_scheduler = &Scheduler{
		jobEventChan:      make(chan *JobEvent, 1000),
		JobPlanTable:      make(map[string]*JobSchedulePlan),
		jobExecutingTable: make(map[string]*JobExecuteInfo),
		jobResultChan:     make(chan *JobExecuteResult, 1000),
	}
	// 启动调度协程
	go G_scheduler.scheduleLoop()
	return
}

// 回传任务执行结果
func (scheduler *Scheduler) PushJobResult(jobResult *JobExecuteResult) {
	scheduler.jobResultChan <- jobResult
}
